#include <rtthread.h>
#include <rtdevice.h>
#include "uc_coding.h"
#include "uc_mask_ope.h"
#include "uc_seg_data_flash_manager.h"

static t_flash_function packet_operate_func = {0};
static t_flash_data_packet segment_data_manager = {0};

/**
 * @brief  call this interface to release memory after data recved and merged
 *
 * @param  data_packet: recved data packet
 *
 */
static void flash_manager_clear(t_flash_data_packet *data_packet)
{
    RT_ASSERT(data_packet != RT_NULL);

    packet_operate_func.free_func(data_packet->flash_addr);

    rt_free(data_packet->segment_mask);

    rt_free(data_packet);
    data_packet = RT_NULL;
}

/**
 * @brief according to src_addr and packet_num returns a t_data_packet structure pointer
 *
 * @param  src_addr: recved data packet
 * @param  packet_num: package No
 * @return t_data_packet structure pointer
 *
 */
static t_flash_data_packet *segdata_packet_find_node(unsigned int src_addr, unsigned char packet_num)
{
    t_flash_data_packet *cur_packet = RT_NULL;

    rt_list_for_each_entry(cur_packet, &segment_data_manager.node, node)
    {
        if (cur_packet->src_addr == src_addr)
        {
            if (cur_packet->packet_num == packet_num)
            {
                return cur_packet;
            }
        }
    }
    return RT_NULL;
}

void flash_segment_packet_init(flash_malloc malloc_func,
                               flash_free free_func,
                               flash_read read_func,
                               flash_write write_func,
                               int segment_data_len)
{
    /* linked list initialization */
    rt_memset(&segment_data_manager, 0, sizeof(t_flash_data_packet));
    rt_list_init(&segment_data_manager.node);

    packet_operate_func.malloc_func = malloc_func;
    packet_operate_func.free_func = free_func;
    packet_operate_func.read_func = read_func;
    packet_operate_func.write_func = write_func;
    packet_operate_func.segment_data_len = segment_data_len;
}

/**
 * @brief package management de initialization
 *
 */
void flash_segment_packet_deinit(void)
{

#if 0
    t_flash_data_packet *temp_packet = RT_NULL;
    rt_list_t *next_node = RT_NULL;

    next_node = segment_data_manager.node.next;

    while (next_node != &segment_data_manager.node)
    {

        temp_packet = rt_list_entry(next_node, t_flash_data_packet, node);
        next_node = next_node->next;

        rt_list_remove(&temp_packet->node);
        data_packet_clear(temp_packet);
    }
    rt_list_init(&segment_data_manager.node);
#else

    rt_list_t *the_node;
    rt_list_for_each(the_node, (&segment_data_manager.node))
    {
        t_flash_data_packet *packet;

        rt_list_remove(the_node);

        packet = rt_list_entry(the_node, t_flash_data_packet, node);

        flash_manager_clear(packet);
    }

#endif
}

/**
 * @brief record data and data length in segmented information

 * @param  data_packet: t_data_packet structure pointer, call function "rr_data_packet_append" to get
 * @param  data: segmented data address
 * @param  data_len: segmented data length
 * @param  cur_seg: current segmented data number
 * @param is_end: recv end flag
 * @return 0:suc, other:fail
 *
 */
static int flash_seg_append(t_flash_data_packet *data_packet, void *data, unsigned int data_len, unsigned short cur_num, unsigned char is_end)
{
    RT_ASSERT(data_packet != RT_NULL);

    // test_data_print("data_packet->segment_mask", (char *)data_packet->segment_mask, (data_packet->total_num + 7) >> 3);

    /* if there are duplicate packages, need to release the previous package first */
    if (GET_BIT(data_packet->segment_mask[(cur_num - 1) >> 3], (cur_num - 1) % 8))
    {
        TRACE_E("cur_num already exists");
        // duplicate package
        return 1;
    }
    else
    {
        // write data to flash
        packet_operate_func.write_func(data_packet->flash_addr + (cur_num - 1) * packet_operate_func.segment_data_len,
                                       data,
                                       data_len);

        data_packet->flash_offset += data_len;

        // set mask bit
        SET_BIT(data_packet->segment_mask[(cur_num - 1) >> 3], (cur_num - 1) % 8);
    }

    data_packet->is_end = is_end;
    data_packet->create_packet_tick = rt_tick_get();

    return 0;
}

/**
 * @brief according to src_addr and packet_num returns a t_data_packet structure pointer
 *
 * @param  src_addr: recved data packet
 * @param  packet_num: package No
 * @return t_data_packet structure pointer
 *
 */
static t_flash_data_packet *segment_packet_append(unsigned int src_addr,
                                                  unsigned char packet_num,
                                                  unsigned short total_num,
                                                  void *user_parament,
                                                  void *(*segment_callback)(void *segment_manager, void *user_parament))
{
    /* according to src_addr and packet_num find node */
    t_flash_data_packet *cur_packet = segdata_packet_find_node(src_addr, packet_num);

    /* if found, return to this node directly */
    if (cur_packet != RT_NULL)
    {
        return cur_packet;
    }
    else
    {
        t_flash_data_packet *new_packet = RT_NULL;
        /* if not found, malloc new node */
        new_packet = (t_flash_data_packet *)rt_malloc(sizeof(t_flash_data_packet));
        if (RT_NULL == new_packet)
        {
            TRACE_E("data_packet_append rt_malloc eror");
            return RT_NULL;
        }
        rt_memset(new_packet, 0, sizeof(t_flash_data_packet));

        new_packet->src_addr = src_addr;
        new_packet->packet_num = packet_num;
        new_packet->total_num = total_num;
        new_packet->create_packet_tick = rt_tick_get();

        new_packet->segment_mask = rt_malloc(RT_ALIGN(GET_BYTE_NUM(total_num), 4));
        RT_ASSERT(new_packet->segment_mask != RT_NULL);
        rt_memset(new_packet->segment_mask, 0, RT_ALIGN(GET_BYTE_NUM(total_num), 4));

        // test_data_print("new data_packet->segment_mask", (char *)new_packet->segment_mask, GET_BYTE_NUM(new_packet->total_num));

        if (RT_NULL != segment_callback)
        {
            void *tmp = segment_callback((void *)new_packet, (void *)user_parament);
            if (RT_NULL != tmp)
            {
                new_packet->parament = tmp;
            }
        }

        new_packet->flash_addr = packet_operate_func.malloc_func(total_num * packet_operate_func.segment_data_len);

        rt_list_insert_after(&segment_data_manager.node, &new_packet->node);

        return new_packet;
    }
}

/**
 * @brief call this interface after recving all segmented data packets, and merge all segmented data packets into a complete data packet
 *
 * @param  data_packet: t_data_packet structure pointer, call function "rr_data_packet_append" to get
 * @param  data: address of the completeed data package after merging
 * @param  data_len: length of the completeed data package after merging
 * @return 0:suc, other:fail
 *
 */
int flash_seg_merge(t_flash_data_packet *data_packet, unsigned int *addr, unsigned int *data_len)
{
    RT_ASSERT(data_packet != RT_NULL);

    for (int n = 0; n < data_packet->total_num; n++)
    {
        if (0 == GET_BIT(data_packet->segment_mask[n >> 3], n % 8))
        {
            return 1;
        }
    }

    *addr = data_packet->flash_addr;
    *data_len = data_packet->flash_offset;

    return 0;
}

/**
 * @brief
 *
 * @return 0:e_segment_parsing_result.
 *
 */
e_segment_parsing_result flash_packege_segment_parsing(void *header,
                                                       void *in_data,
                                                       unsigned int in_data_len,
                                                       unsigned int *out_data_addr,
                                                       unsigned int *out_data_len,
                                                       void *user_parament,
                                                       void *(*segment_callback)(void *segment_manager, void *user_parament),
                                                       void **get_manager_page)

{
    app_ps_header_t *ps_header = header;
    if (ps_header->property.segment_flag)
    {
        t_flash_data_packet *manager_packet = segment_packet_append(ps_header->addr.src_addr,
                                                                    ps_header->packet_num,
                                                                    ps_header->segment_info.total_num,
                                                                    user_parament,
                                                                    segment_callback);

        if (RT_NULL != manager_packet)
        {
            *get_manager_page = (void *)manager_packet;
        }
        if (0 != flash_seg_append(manager_packet, in_data, in_data_len, ps_header->segment_info.current_num, ps_header->segment_info.is_end))
            return SEGMENT_PARSING_APPEND_FAIL;

        if (manager_packet->is_end == SEGMENT_HAVE_END)
        {
            if (0 != flash_seg_merge(manager_packet, out_data_addr, out_data_len))
                return SEGMENT_PARSING_MERGE_FAIL;
            else
            {
                return SEGMENT_PARSING_SUCCESS;
            }
        }
    }

    return SEGMENT_PARSING_OTHER_FAIL;
}

/**
 * @brief
 * @param src_addr: the original address of the device that sent the subpacket.
 * @param packet_num: the sequence number of the package that was subpackaged.
 * @param mask: packet loss data identified by bits.
 * @param pos: the array subscript obtained according to the byte mask
 * @param mask_len: gets the valid length of the mask array
 * @param loss_num: the number of valid packet drops.
 *
 * @return 0:success 1: fail
 *
 */
int flash_get_loss_block(unsigned int src_addr,
                         unsigned char packet_num,
                         unsigned char **mask,
                         int *pos,
                         int *mask_len,
                         int *loss_num)
{
    // app_ps_header_t *ps_header = header;
    unsigned char case_flag = 0;
    unsigned char start_index = 0;
    unsigned char end_index = 0;
    t_flash_data_packet *manager_packet = segdata_packet_find_node(src_addr, packet_num);

    if (RT_NULL != manager_packet)
    {
        // test_data_print("segment_mask", (char *)manager_packet->segment_mask, (manager_packet->total_num + 7) >> 3);

        for (int n = 0; n < manager_packet->total_num; n++)
        {
            if (0 == GET_BIT(manager_packet->segment_mask[n >> 3], n % 8))
            {
                (*loss_num)++;
                // TRACE_I("[dtu] d case %d n %d loss_num %d", case_flag, n, *loss_num);
                switch ((int)case_flag)
                {
                case 0:
                {
                    *pos = (n >> 3);
                    *mask = &(manager_packet->segment_mask[n >> 3]);
                    start_index = (n >> 3);
                    end_index = start_index;
                    case_flag = 1;
                    break;
                }
                case 1:
                {
                    end_index = (n >> 3);
                    break;
                }
                }
            }
        }

        *mask_len = 1 + end_index - start_index;
    }
    TRACE_D("[dtu] d mask_len %d case_flag %d", *mask_len, case_flag);

    return case_flag == 0 ? 1 : 0;
}

/**
 * @brief
 *
 * @return 0:fail
 *
 */
unsigned int flash_get_packet_tick(unsigned int addr, unsigned char packet_num)
{
    t_flash_data_packet *data_packet = segdata_packet_find_node(addr, packet_num);

    if (RT_NULL != data_packet)
        return data_packet->create_packet_tick;

    return 0;
}

/**
 * @brief
 *
 * @return 0:fail
 *
 */
unsigned int flash_get_segment_manager(unsigned int addr, unsigned char packet_num)
{
    return (unsigned int)segdata_packet_find_node(addr, packet_num);
}

/**
 * @brief  call this interface to release memory after data recved and merged
 *
 * @param  data_packet: recved data packet
 *
 */
void flash_seg_packet_clear(unsigned int addr, unsigned char packet_num)
{
    rt_list_t *the_node;
    rt_list_for_each(the_node, (&segment_data_manager.node))
    {
        t_flash_data_packet *packet_node;
        packet_node = rt_list_entry(the_node, t_flash_data_packet, node);

        if (packet_node->src_addr == addr &&
            packet_node->packet_num == packet_num)
        {
            rt_list_remove(the_node);

            flash_manager_clear(packet_node);
            break;
        }
    }
}
